#include "MemUtil.h"
#include "Tracer.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

namespace libemb{

MemPool::MemPool()
{
}

MemPool::~MemPool()
{
    free(m_memory);
}

bool MemPool::init(int blockNum, int blockSize,void* memStart)
{
    if (blockNum<=0 || blockSize<=0) 
    {
        return false;
    }
    m_blockNum = blockNum;
    m_blockSize = blockSize;
    m_poolSize = m_blockNum * m_blockSize; 
	if (memStart)
    {
    	m_memory = memStart;
	}
	else
	{
		m_memory = calloc(m_blockNum,m_blockSize);
	    if (m_memory==NULL) 
	    {
	        return false;
	    }
	}
    for (auto i=0; i < m_blockNum; i++) 
    {
        auto memBlock =  std::make_unique<MemBlock>();
        memBlock->m_isUsed = false;
        memBlock->m_address = m_memory+(i*m_blockSize);
        m_memBlocks.push_back(std::move(memBlock));
    }
    return true;
}

void* MemPool::getMemory(const std::string& memoryName, int memorySize)
{
    if (memorySize <= 0 || m_blockSize <= 0 || m_poolSize<=0 || m_blockNum<=0 || memoryName.empty()) 
    {
        return NULL;
    }
    int blocks = (memorySize + m_blockSize-1)/m_blockSize;
    int num=0;
    for (auto i=0; i < m_blockNum; i++) 
    {
        if (!m_memBlocks[i]->m_isUsed) 
        {
            num++;
            if (num==blocks) 
            {
                for (auto j=0;j<blocks;j++) 
                {
                    m_memBlocks[i-j]->m_isUsed=true;
                    m_memBlocks[i-j]->m_name = memoryName;
                }
                return m_memBlocks[i-blocks+1]->m_address;
            }
        }
        else
        {
            num=0;
        }
    }
    return NULL;
}

bool MemPool::putMemory(const std::string& memoryName)
{
    if (m_blockSize <= 0 || m_poolSize<=0 || m_blockNum<=0) 
    {
        return false;
    }
    for (auto i=0; i<m_blockNum; i++) 
    {
        if (m_memBlocks[i]->m_name==memoryName && 
            m_memBlocks[i]->m_isUsed) 
        {
            m_memBlocks[i]->m_name="";
            m_memBlocks[i]->m_isUsed=false;
        }
    }
    return true;
}
void MemPool::showMemory()
{
    for (auto i=0; i<m_blockNum; i++)
    {
        TRACE_DBG("name:%s,use:%d,block:%d,addr:%08x",
                  CSTR(m_memBlocks[i]->m_name),m_memBlocks[i]->m_isUsed,
                  i,m_memBlocks[i]->m_address);
    }
}

MemShared::MemShared(int type):
m_type(type)
{
}
MemShared::~MemShared()
{
	close();
}
bool MemShared::open(std::string name, int size)
{
    if (name.empty())
	{
		TRACE_ERR_CLASS("not specify memory name!");
		return false;
	}
    
    if (m_fd >= 0)
	{
		TRACE_ERR_CLASS("memory already exists!");
		return false;
	}
	/* 打开共享文件(如果不存在则创建) */
    if(m_type==TYPE_SHM)
	{
    	m_fd = ::shm_open(CSTR(name), O_RDWR|O_CREAT,0666);
    }
	else
	{
		m_fd = ::open(CSTR(name), O_RDWR|O_CREAT,0666);
	}

	if (m_fd<0)
	{
		TRACE_ERR_CLASS("open memory:%s, error: %s",CSTR(name),ERRSTR);
		return false;
	}
	
	/* 设置文件大小 */
	if (size>0 && ftruncate(m_fd,size)<0)
    {
        TRACE_ERR_CLASS("shm[%s] set size(%d) error:%s!",CSTR(name),m_size,ERRSTR);
		::close(m_fd);
		m_fd = -1;
        return false;    
    }
	m_size = this->size();
    m_name = name;
	return true;  
}

bool MemShared::close()
{
    if (m_type==TYPE_SHM)
    {
        if (shm_unlink(CSTR(m_name))!=0)
        {
            TRACE_ERR_CLASS("shm[%s] unlink error:%s!",CSTR(m_name),ERRSTR);
            return false;
        }
    }
    else
    {
        if ((m_fd>0)&&(::close(m_fd)!=0))
        {
            TRACE_ERR_CLASS("shm file[%s] close error:%s!",CSTR(m_name),ERRSTR);
            return false;
        }
    }
    return true;
}

/**
 *  @brief  创建并绑定共享内存
 *  @param  none
 *  @return 成功返回内存地址,失败返回NULL
 *  @note   none
 */
void* MemShared::attach()
{    
	if (m_address!=NULL)
	{
		TRACE_ERR_CLASS("memory allready attached!");
		return NULL;
	}
	if (m_fd<0 || m_size<=0)
	{
		TRACE_ERR_CLASS("memory not opened!");
		return NULL;
	}
    void* addr = mmap(NULL,m_size,PROT_READ|PROT_WRITE,MAP_SHARED,m_fd,0);
    if (MAP_FAILED==addr)
    {
        TRACE_ERR_CLASS("memory map[%s], size:%d, error:%s!",CSTR(m_name),m_size,ERRSTR);
        return NULL;    
    }
    return m_address=addr;
}

/**
 *  @brief  销毁共享内存区域
 *  @param  none
 *  @return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  @note   none
 */
int MemShared::detach()
{
    if (m_address!=NULL && munmap(m_address,m_size)!=0)
    {
    	TRACE_ERR_CLASS("memory unmap[%s], error:%s",CSTR(m_name),ERRSTR);
        return RC_ERROR;   
    }
	m_address = NULL;
    return RC_OK;
}

/* 获取内存大小 */
int MemShared::size()
{
    struct stat fdStat;
    if (m_fd<0 || fstat(m_fd,&fdStat)<0)
    {
    	return 0;	
    }
    return fdStat.st_size;
}
/* 获取内存地址 */
void* MemShared::address()
{
	return m_address;
}

}
